package middlewares

import (
	"gitee.com/liuxiaowei123/vblog-3/app/token"
	"gitee.com/liuxiaowei123/vblog-3/app/user"
	"gitee.com/liuxiaowei123/vblog-3/exception"
	"gitee.com/liuxiaowei123/vblog-3/ioc"
	"gitee.com/liuxiaowei123/vblog-3/response"
	"github.com/gin-gonic/gin"
)

// 此文件用于实现中间件认证功能

type TokenAuther struct {
	tksvc token.TokenService
	// 传入的角色，也就是执行api接口层的方法时需要传入的参数
	role user.Role
}

func NewTokenAuther() *TokenAuther {
	return &TokenAuther{
		tksvc: ioc.Controller().Get(token.Appname).(token.TokenService),
	}
}

// 认证功能
func (t *TokenAuther) Auth(c *gin.Context) {
	// 1、从HTTP请求的Cookie中获取指定名称的值
	tkcookie, err := c.Cookie(token.TOKEN_COOKIE_NAME)
	if err != nil {
		response.Failed(c, exception.NewNotFoundCookie("没有发现cookie中的值: %s", token.TOKEN_COOKIE_NAME))
		return
	}
	// 2、校验cookie中的值，并返回一个token
	req := token.NewValidateRequest()
	req.AccessToken = tkcookie
	tk, err := t.tksvc.Validate(c.Request.Context(), req)
	if err != nil {
		response.Failed(c, err)
	}
	// 3、存储校验后的键值对，便于从上下文中获取值
	if c.Keys == nil {
		// c.Keys 是 nil，它不表示一个空的 map[string]any，而是表示该字段尚未被初始化
		c.Keys = map[string]any{}
		c.Keys[token.TOKEN_GIN_KEY_NAME] = tk
	}
}

// 鉴权功能
func (t *TokenAuther) Perm(c *gin.Context) {
	//1、获取认证后的token用户
	gintoken := c.Keys[token.TOKEN_GIN_KEY_NAME]
	if gintoken == nil {
		response.Failed(c, exception.NewNotFoundGinCookie("没有通过gin上下文获取到token"))
		return
	}
	//2、判断token中的role和传入的role是否一致
	v, ok := gintoken.(*token.Token)
	if !ok {
		response.Failed(c, exception.NewNotFoundGinCookie("没有通过gin上下文获取到的值不是token类型"))
		return
	}
	// 2.1、传入的是管理员role，直接放行
	if v.Role == user.ROLE_ADMIN {
		// 当return语句被执行时，此函数或方法会立即终止
		return
	}
	// 2.2、传入的role和token中的角色不一致，则拦截
	if v.Role != t.role {
		response.Failed(c, exception.NewNotPerm("权限不足，不能执行此操作"))
		return
	}
}

// 实现向鉴权中间件传入参数；通过下面扩展中间件结构体的方法实现此功能也是可以的
func Required(r user.Role) gin.HandlerFunc {
	t := NewTokenAuther()
	t.role = r
	return t.Perm
}

// func (t *TokenAuther) Required(r user.Role) gin.HandlerFunc {
// 	t.role = r
// 	return t.Perm
// }
